<?php


namespace App\Service\Admin;


use _HumbugBox5f943a942674\Nette\DI\Extensions\DIExtension;
use App\Constants\ErrorCode;
use App\Constants\Model\CooperativeMenuConfig;
use App\Constants\Model\Cooperative as CooperativeConstants;
use App\Constants\Model\CooperativeUser;
use App\Dao\CooperativeDao;
use App\Dao\CooperativeMenuDao;
use App\Dao\CooperativeRoleDao;
use App\Dao\CooperativeUserDao;
use App\Exception\BusinessException;
use App\Model\Cooperative;
use App\Model\CooperativeMenu;
use App\Model\CooperativeRole;
use App\Utils\Casbin;
use App\Utils\Log;
use App\Utils\Notice\CooperativeAuditReturn;
use App\Utils\Notice\Sender;
use App\Utils\Wechat\OpenPlatform\MiniProgram;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Database\Model\Collection;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Server\Exception\RuntimeException;
use Hyperf\Utils\Codec\Xml;

class CooperativeService extends BaseService
{

    /**
     * @Inject()
     * @var CooperativeDao
     */
    protected $cooperativeDao;

    /**
     * @Inject()
     * @var CooperativeMenuDao
     */
    protected $cooperativeMenuDao;

    /**
     * @Inject()
     * @var CooperativeUserDao
     */
    protected $cooperativeUserDao;

    /**
     * @Inject()
     * @var CooperativeRoleDao
     */
    protected $cooperativeRoleDao;

    /**
     * @var Casbin
     */
    protected $casbinInstance;


    public function __construct()
    {
        $this->casbinInstance = new Casbin(config('casbin.cooperative'));
    }

    public function index(array $where)
    {
        $pageInfo = $this->extractPaginateInfo($where);
        $where = $this->formatWhereRegionToCooperative($where);
        $index = $this->cooperativeDao
            ->where($where)
            ->withCount('tasks')
            ->withCount('users')
            ->withCount('employers')
            ->paginate(
                $pageInfo['perPage'], ['*'], 'page', $pageInfo['page']
            );
        return $this->formatIndex($index);
    }

    public function formatIndex(LengthAwarePaginatorInterface $index)
    {
        $data = [];
        /** @var Cooperative $cooperative */
        foreach ($index->items() as $cooperative) {
            $data[] = [
                'cooperative_id' => $cooperative->id,
                'cooperative_status' => $cooperative->status,
                'cooperative_name' => $cooperative->nickname,
                'cooperative_avatar' => $cooperative->avatar,
                'cooperative_phone' => $cooperative->phone,
                'cooperative_tasks_count' => $cooperative->tasks_count,
                'cooperative_users_count' => $cooperative->users_count,
                'cooperative_employers_count' => $cooperative->employers_count,
                'client_url' => $cooperative->clientUrl,
                'created_at' => $cooperative->created_at->toDateTimeString(),
            ];
        }

        return [
            'data' => $data,
            'current_page' => $index->currentPage(),
            'total' => $index->total(),
            'per_page' => $index->perPage()
        ];
    }

    public function auditIndex(array $where)
    {
        $pageInfo = $this->extractPaginateInfo($where);
        $where = $this->formatWhereRegionToCooperative($where);
        $index = $this->cooperativeDao
            ->where($where)
            ->paginate(
                $pageInfo['perPage'], ['*'], 'page', $pageInfo['page']
            );
        return $this->formatAuditIndex($index);
    }

    public function formatAuditIndex(LengthAwarePaginatorInterface $index)
    {
        $data = [];
        /** @var Cooperative $cooperative */
        foreach ($index->items() as $cooperative) {
            $data[] = [
                'cooperative_id' => $cooperative->id,
                'cooperative_name' => $cooperative->nickname,
                'cooperative_phone' => $cooperative->phone,
                'cooperative_region' => $cooperative->regionString,
                'cooperative_audit_info' => [
                    'cooperative_wx_merchant_secret' => $cooperative->wx_merchant_secret,
                    'cooperative_wx_merchant_code' => $cooperative->wx_merchant_code,
                    'cooperative_wx_cert' => $cooperative->wx_cert
                ],
                'cooperative_audit_status' => $cooperative->audit_status,
                'created_at' => $cooperative->created_at->toDateTimeString()
            ];
        }

        return [
            'data' => $data,
            'current_page' => $index->currentPage(),
            'total' => $index->total(),
            'per_page' => $index->perPage()
        ];
    }

    public function block(int $cooperativeId)
    {
        $cooperative = $this->cooperativeDao->firstOrThrow([
            'id' => $cooperativeId,
            'status' => CooperativeConstants::STATUS_UNBLOCK
        ], ErrorCode::COOPERATIVE_NOT_FOUND);
        $cooperative->status = CooperativeConstants::STATUS_BLOCKED;
        return $cooperative->save();

    }

    public function unblock(int $cooperativeId)
    {
        $cooperative = $this->cooperativeDao->firstOrThrow([
            'id' => $cooperativeId,
            'status' => CooperativeConstants::STATUS_BLOCKED
        ], ErrorCode::COOPERATIVE_NOT_FOUND);
        $cooperative->status = CooperativeConstants::STATUS_UNBLOCK;
        return $cooperative->save();
    }

    /**
     * 获取合作商权限
     * @param int $cooperativeId
     */
    public function getMenus(int $cooperativeId)
    {
        /**
         * @var $cooperative Cooperative
         */
        $cooperative = $this->cooperativeDao->first(['id' => $cooperativeId]);

        return $this->formatGetMenus($cooperative->cooperativeMenus);
    }

    public function formatGetMenus(Collection $collection)
    {
        $data = [];
        /** @var CooperativeMenu $cooperativeMenu */
        foreach ($collection as $cooperativeMenu) {
            $data[] = [
                'menu_config_id' => $cooperativeMenu->menu_config_id,
                'title' => $cooperativeMenu->title,
                'name' => $cooperativeMenu->name
            ];
        }

        return $data;
    }

    /**
     * 设置合作商权限
     * @param int $cooperativeId
     * @param array $cooperativeMenuConfigIds
     */
    public function setMenus(int $cooperativeId, array $cooperativeMenuConfigIds)
    {
        /**
         * @var $cooperative Cooperative
         */
        $cooperative = $this->cooperativeDao->firstOrThrow(['id' => $cooperativeId]);

        //当前的可设置权限
        $currentCooperativeMenus = $cooperative->cooperativeMenus;

        $currentCooperativeMenuConfigIds = $currentCooperativeMenus->pluck('menu_config_id')->toArray();

        //待增加的可设置权限
        $addCooperativeMenuConfigIds = array_diff(
            $cooperativeMenuConfigIds,
            $currentCooperativeMenuConfigIds
        );

        //待移除的可设置权限
        $removeCooperativeMenuConfigIds = array_diff(
            $currentCooperativeMenuConfigIds,
            $cooperativeMenuConfigIds
        );
        $removeCooperativeMenus = $cooperative->cooperativeMenus()->whereIn(
            'menu_config_id',
            $removeCooperativeMenuConfigIds
        )->get();

        Db::beginTransaction();
        try {
            $cooperative->cooperativeMenus()->whereIn('menu_config_id', $removeCooperativeMenuConfigIds)->delete();

            foreach ($addCooperativeMenuConfigIds as $addCooperativeMenuConfigId) {
                $cooperative->cooperativeMenus()->save(
                    new CooperativeMenu(['menu_config_id' => $addCooperativeMenuConfigId])
                );
            }
            $addCooperativeMenus = $cooperative->cooperativeMenus()->whereIn(
                'menu_config_id',
                $addCooperativeMenuConfigIds
            )->get();

            //更新该合作商下所有角色的权限
            $cooperativeRoles = $cooperative->cooperativeRoles;

            $removeCooperativeMenuIds = $removeCooperativeMenus->pluck('id')->toArray();
            /** @var CooperativeRole $cooperativeRole  */
            foreach ($cooperativeRoles as $cooperativeRole) {

                //重新设置角色casbin权限 如果是超级管理员 则等于当前合作商剩余的全部权限
                if ($cooperativeRole->name == config('menus.default_cooperative_role.name')) {
                    $cooperativeRole->cooperativeMenus()->detach($removeCooperativeMenuIds);
                    $cooperativeRole->cooperativeMenus()->attach($addCooperativeMenus->pluck('id')->toArray());
                    $roleCooperativeMenus = $cooperative->cooperativeMenus()->whereNotIn('menu_config_id', $removeCooperativeMenuConfigIds)->get();
                } else {
                    $cooperativeRole->cooperativeMenus()->detach($removeCooperativeMenuIds);
                    $roleCooperativeMenus = $cooperativeRole->cooperativeMenus;
                }

                $rolePermissions = [];
                /** @var CooperativeMenu $roleCooperativeMenu */
                foreach ($roleCooperativeMenus as $roleCooperativeMenu) {
                    if (CooperativeMenuConfig::TYPE_API == $roleCooperativeMenu->type) {
                        $rolePermissions[] = $roleCooperativeMenu->api;
                    }
                }
                $this->casbinInstance->setPermissionsForRole($cooperativeRole->id, $rolePermissions);
            }


            Db::commit();

        } catch (\Exception $exception) {
            Db::rollBack();
            Log::get()->error($exception->getMessage(), $cooperativeMenuConfigIds);
            if ($exception instanceof BusinessException) {
                throw new BusinessException($exception->getCode(), $exception->getMessage());
            }
            throw new \Exception($exception->getCode(), $exception->getMessage());
        }

    }

    /**
     * @param int $id
     * @param array $params
     * @return bool
     */
    public function audit(int $id, array $params)
    {
        /** @var Cooperative $cooperative */
        $cooperative = $this->cooperativeDao->first(['id' => $id]);
        if (!$cooperative) {
            throw new BusinessException(ErrorCode::COOPERATIVE_NOT_FOUND);
        }
        $cooperative->audit_status = $params['audit_status'];
        if (isset($params['audit_message'])) {
            $cooperative->audit_message = $params['audit_message'];
        }

        $cooperative->save();

        if ($params['audit_status'] == \App\Constants\Model\Cooperative::AUDIT_STATUS_RETURN) {
            $cooperativeUser = $this->cooperativeUserDao->first([
                'cooperative_id' => $cooperative->id,
                'is_manager' => CooperativeUser::IS_MANAGER_YES
            ]);
            //合作商审核不通过通知
            Sender::send($cooperativeUser->id, new CooperativeAuditReturn(['replaces' => [
                'message' => $params['audit_message']
            ]]));
        }

        return true;
    }

    /**
     * @param $id
     * @param int $templateID
     * @return bool
     */
    public function submitAudit($id, int $templateID)
    {
        /** @var Cooperative $cooperative */
        $cooperative = $this->cooperativeDao->first(['id' => $id]);

        if ($cooperative->audit_status == CooperativeConstants::AUDIT_STATUS_PASS
            && $cooperative->bind_mp == CooperativeConstants::BIND_MP_YES) {
            $extData['extEnable'] = true;
            $extData['extAppid'] = $cooperative->wx_mp_appid;
            $extData['ext']['headerDomainField'] = 'Domain';
            $extData['ext']['headerDomainValue'] = $cooperative->domain;
            Log::get()->info('[extData]' . json_encode($extData));
            try {
                $res = di(MiniProgram::class)->commit($cooperative->wx_mp_appid, $templateID, json_encode($extData));
                Log::get()->info(json_encode((array)$res));
                if ($res['errcode'] == 0 && strtolower($res['errmsg']) == 'ok') {
                    $res2 = di(MiniProgram::class)->submitAudit($cooperative->wx_mp_appid);
                    Log::get()->info(json_encode((array)$res2));
                }
            } catch (\Throwable $e) {
                Log::get()->error($e->getTraceAsString());
                throw new RuntimeException($e->getMessage(), $e->getCode());
            }
        }
        return true;
    }
}